package org.common.utils.net.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.common.utils.net.netty.core.NettyClientEventListener;
import org.common.utils.net.netty.core.NettyInitializer;
import org.common.utils.thread.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;

/**
 * 基于Netty4的客户端
 * @author zhouzhibing
 * @date 2025/11/13 14:36
 */
public class NettyClient {

    private static final Logger log = LoggerFactory.getLogger(NettyClient.class);
    /**
     * 重连延迟，单位秒。
     */
    public static final int RECONNECT_DELAY = 5;
    /**
     * 服务器地址
     */
    private final String host;
    /**
     * 服务器端口
     */
    private final int port;
    /**
     * 事件组
     */
    private EventLoopGroup group;
    /**
     * 启动引导
     */
    private Bootstrap bootstrap;
    /**
     * 客户端通道
     */
    private Channel channel;
    /**
     * 初始化器
     */
    private final NettyInitializer initializer;
    /**
     * 客户端事件监听器
     */
    private NettyClientEventListener eventListener;
    /**
     * 附加对象
     */
    private Object attach;
    /**
     * 是否已开始
     */
    private boolean started;
    /**
     * toString 字符串
     */
    private String toString;

    public NettyClient(NettyInitializer initializer, String host, int port) {
        this.initializer = initializer;
        this.host = host;
        this.port = port;
        initBootstrap();
    }

    private void initBootstrap() {
        group = new NioEventLoopGroup(new DefaultThreadFactory(initializer.getName() + "-NettyClient"));
        bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .handler(initializer);
    }

    /**
     * 启动客户端
     */
    public void start() {
        if(started) {
            log.warn("客户端已启动 {}" , this);
            return;
        }
        started = true;
        doConnect();
    }

    /**
     * 执行服务器
     */
    private void doConnect() {
        //未连接或连接失败，进行重新连接。
        if(channel == null || !channel.isActive()) {
            bootstrap.connect(host, port).addListener((ChannelFuture future) -> {
                if (future.isSuccess()) {
                    if(channel != null) {
                        toString = null;
                        channel.close();
                    }

                    channel = future.channel();
                    log.info("连接服务器成功 {}", this);
                    triggerEvent(Event.CONNECT_SUCCESS);
                } else {
                    log.warn("连接服务器失败 {}", this);
                    triggerEvent(Event.CONNECT_FAIL);
                }
                future.channel().eventLoop().schedule(this::doConnect, RECONNECT_DELAY, TimeUnit.SECONDS);
            });
        } else {
            //已连接，则继续检查连接状态。
            channel.eventLoop().schedule(this::doConnect, RECONNECT_DELAY, TimeUnit.SECONDS);
        }
    }

    /**
     * 发送消息
     * @param message 消息
     */
    public void sendMessage(Object message) {
        if (channel != null && channel.isActive()) {
            channel.writeAndFlush(message);
            log.info("send message success , channel = {} , message = {}" , channel , message);
        } else {
            log.warn("send message fail , channel = {} , message = {}" , channel , message);
            triggerEvent(Event.SEND_MESSAGE_FAIL);
        }
    }

    /**
     * 关闭客户端
     */
    public void shutdown() {
        if (channel != null) {
            channel.close();
        }
        if (group != null) {
            group.shutdownGracefully();
        }
        triggerEvent(Event.SHUT_DOWN);
        log.info("客户端已关闭");
    }

    /**
     * 该客户端连接是否已连接，活跃中。
     * @return 是否已连接状态
     */
    public boolean isActive() {
        return channel != null && channel.isActive();
    }

    /**
     * 设置附加对象
     * @param attach 附加对象
     */
    public void setAttach(Object attach) {
        this.attach = attach;
    }

    /**
     * @return 获取附加对象
     */
    public <T> T getAttach() {
        return (T)attach;
    }

    /**
     * 设置客户端事件监听器，@like Event
     * @param eventListener 事件监听器
     */
    public void setEventListener(NettyClientEventListener eventListener) {
        this.eventListener = eventListener;
    }

    /**
     * 触发事件
     * @param event 事件枚举对象
     */
    private void triggerEvent(Event event) {
        if (eventListener != null) {
            eventListener.onEvent(this , event);
        }
    }

    @Override
    public String toString() {
        if(channel == null) {
            return "NettyClient{host=" + host + ", port=" + port + "}";
        }
        if(toString != null)
            return toString;

        toString = attach == null ? channel.toString() : channel + "#" + attach;
        return toString;
    }

    /**
     * 客户端事件
     */
    public enum Event {
        /**
         * 连接成功
         */
        CONNECT_SUCCESS,
        /**
         * 连接失败
         */
        CONNECT_FAIL,
        /**
         * 发送消息失败
         */
        SEND_MESSAGE_FAIL,
        /**
         * 关闭
         */
        SHUT_DOWN,
    }

}
